/* Copyright (c) 2001 Nextel Communications, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *   - Neither the name of Nextel Communications, Inc. nor the names of its
 *     contributors may be used to endorse or promote products derived from
 *     this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NEXTEL
 * COMMUNICATIONS, INC. OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.nextel.ui; // Generated package name
import com.nextel.ui.custom.OListenerScroll;
import com.nextel.util.Debug;
import com.nextel.util.Logger;
import com.nextel.util.lwuit.Font;
import java.io.IOException;
//import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;

/**
 *  A screen that allows the entry of text.
 * <p>
 * The component can be set to accept uppercase letters, lowercase letters,
 * numbers only, or any combination of the three. The desired behavior is
 * enabled via the {@link #OTextScreen constructor}. The component can
 * also be set to accept special characters, such as @ and $, via
 * {@link #allow}.
 * <p>
 * The character sets for each number
 * key, and the behavior exhibited when a number key is pressed,
 * are defined in {@link com.nextel.ui.OInputComposer}
 * <p>
 * <p>
 * The user can move within the text field
 * by pressing the left (previous character) and right (next character)
 * sides of the 4-way navigation key. The top and bottom sides of the 4-way
 * navigation key are therefore used to move to the previous and next component
 * on the screen, whereas with most other components the left and right sides of
 * the 4-way navigation key are used to move to the previous and next components.
 * <p>
 * A vertical cursor is displayed before the position where the next character
 * is to be placed.
 * <p>
 * The asterisk <b>*</b> key is used to perform backspace/delete
 * operations on the
 * text field.  When the asterisk key is depressed, the preceding character is
 * erased and the remaining characters are shifted left.  The cursor position
 * is updated to reflect the deleted character.
 * <p>
 * The pound <b>#</b> key is used to insert spaces into the
 * text field.
 * <p>
 * If the text exceeds the vertical size of the display area up and down arrows,
 * as appropriate, will display at the bottom of the screen, and
 * the display will scroll vertically when the cursor is moved to a line that is
 * not displayed.
 * <p>
 * An optional
 * screen title can be supplied, and both the title and text can have their text
 * font specified.  The text will be wrapped automatically to ensure each line
 * fits width-wise into the display area.  You may also place '\n'
 * characters where line breaks are desired.
 * <p>
 * The field will also accept input when the handset is connected to the iBoard,
 * Motorola's foldable keyboard. However, because there is no way for the
 * component to know that it is attached to the iBoard, keys that issue
 * identical codes on the handset and iBoard will have handset behavior even
 * when pressed on the iBoard. For example, since the handset include number
 * keys, and multiple presses of a number key on the handset cycle through
 * available characters as described in {@link com.nextel.ui.OTextDocument},
 * pressing a number key on the iBoard will cycle through the same set of
 * character. Likewise, pressing the * and # keys on the iBoard result in the
 * insertion of a space or deletion of a character, respectively, because that
 * is the function of those keys on the handset.
 *
 * @author Glen Cordrey
 */
public class OTextScreen extends OAbstractScreen  implements OListenerScroll{
    // the maximum number of lines that can be placed in a screen. This number was
    // arbitrarily chosen for sizing purposes
    
    private static final int MAX_NBR_OF_LINES = 50;
    // anchor point for writing text
//    private final static int ANCHOR = Graphics.BOTTOM | Graphics.LEFT;
    /** Number of pixels for a left margin */
    private static final int LEFT_MARGIN = 2;

    private static Font textFont;
    // the number of lines that can be displayed in the screen's body area
    private int nbrDisplayableLines;
    // the index in the list of the first line displayed
    private int firstDisplayedLineIdx = 0;
    // the index in the list of the last line displayed
    private int lastDisplayedLineIdx = 0;
    private StringBuffer textBuff; // the text in the box

    // array of the end indices of the lines in the text. only the indices of the
    // displayed lines are kept current, the indices for the other lines get
    // recalculated when those lines get displayed
    private int[] lineEndIndices = new int[MAX_NBR_OF_LINES];
    // the index of the line that the cursor is on
    private int editingLineIdx;
    // the minimum number of characters in a line, calculated from the font and
    // the widest character
//    private int minCharsInLine;
    private int nbrOfChars; // max # of characters in the box
    // whether the text box can be edited
//    private boolean editable = true;
    private final int screenWidth = getWidth();
    // the constraints on the input characters, defined in OTextDocument
//    private int constraints;
    // special characters, other than letters and digits,
    // that are allowed to be input
    private String specialChars = new String();

    private String nombreImagen;

    
    public void setNombreImagen(String name) {
        nombreImagen = name;
    }
    
    /**
     * Creates a new <code>OTextScreen</code> instance.
     *
     * @param title Screen title, which can be null.
     * @param titleFont Font for screen title, which should be null if the title
     * is null but not if it isn't.
     * @param text The text to display. The text may contain '\n' characters to
     * specify line breaks.
     * @param textFont The font to use for displaying the font.
     * @param nbrOfChars Maximum number of characters in the text box.
     * @param constraints {@link com.nextel.ui.OTextDocument#ANY}, or a
     * bitwise-or of any of {@link com.nextel.ui.OTextDocument#UPPERCASE},
     *{@link com.nextel.ui.OTextDocument#LOWERCASE}, and
     * {@link com.nextel.ui.OTextDocument#NUMERIC}
     *
     */
    public OTextScreen(String title, Font titleFont,
            String text, Font textFont, int nbrOfChars,
            int constraints) {
        super(title, titleFont);
        if (Debug.ON) {
            Logger.dev("OTextScreen.OTextScreen ENTERED");
        }
        OTextScreen.textFont = textFont;
        this.nbrDisplayableLines = getBodyHeight() / textFont.getHeight();
//        this.minCharsInLine = getWidth() / textFont.charWidth(widestChar);
        this.nbrOfChars = nbrOfChars;
//        this.constraints = constraints;
        
        textBuff = new StringBuffer(nbrOfChars);
        if (text != null) { // text was supplied
            textBuff.insert(0, text);
            updateLineIndices(0);
        }
        
    } // constructor
    
    /**
     * Creates a non-editable text screen.
     * <p>
     * This method calls {@link #OTextScreen(String, Font, String, Font, int, int )}
     * with <code>nbrOfChars</code> equal to the length of <code>text</code> and
     * <code>constraints</code> equal to
     * {@link com.nextel.ui.OTextDocument#ANY}, and calls
     * {@link #setEditable setEditable( false )}.
     *
     * @param title Screen title, which can be null.
     * @param titleFont Font for screen title, which should be null if the title
     * is null but not if it isn't.
     * @param text The text to display. The text may contain '\n' characters to
     * specify line breaks.
     * @param textFont The font to use for displaying the font.
     */
    public OTextScreen(String title, Font titleFont,
            String text, Font textFont) {
        this(title, titleFont, text, textFont, text.length(),
                OTextDocument.ANY);
    } // OTextScreen
    
    
    protected void paintBody(Graphics g) {
        
        com.nextel.util.lwuit.Graphics grap = new com.nextel.util.lwuit.Graphics(g);
        
        super.paintBodyBackground(g);
        grap.setFont(textFont);
        grap.setColor(OUILook.TEXT_COLOR);
        
        // draw each text line within the window of lines to display
        int y = getBodyRow();
        String nextLine = null;
//        int anchoimgen = 0;
        int altoImagen = 0;
        
        
        if (nombreImagen != null) {
            try {
                //
                Image image = Image.createImage(nombreImagen);
                
                altoImagen = image.getHeight( ) ;
                g.drawImage(image, 2, 16, Graphics.LEFT | Graphics.TOP);
                y = y + altoImagen;
                
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        
        if (textBuff.length() > 0) {
            for (int idx = firstDisplayedLineIdx;
            idx <= lastDisplayedLineIdx; idx++) {
                
                int lineStart = 0;
                if (idx > 0) {
                    lineStart = getLineStart(idx);
                }
                int lineEnd = lineEndIndices[idx];
                
                if (lineEnd  > lineStart  && lineEnd < textBuff.length()) {
                    char[] lineChars = new char[lineEnd - lineStart + 1];
                    textBuff.getChars(lineStart, lineEnd +1  , lineChars, 0);
                    nextLine = new String(lineChars);
                    
//                    System.out.println(" nextLine =" + nextLine);
                    
                    grap.drawString(nextLine, LEFT_MARGIN , y);
                    y += textFont.getHeight();
                    
                } else {
                    break;
                }
                
            }
        }
        
 
    } // paintBody
    
    /**
     * Determines whether scrolling is needed and in which directions.
     * <p>
     * This overrides the parent method because the parent method determines
     * scrolling via the components the class contains, and this class doesn't
     * contain any components.
     *
     * @return an <code>int</code> value
     */
    protected int getScrollDirections() {
        
        // if text can be scrolled indicate so
        int directions = 0;
        
        if (!lastLine(lastDisplayedLineIdx)) { // there are lines after the last displayed line, so need down scroller
            directions += OComponent.DOWN;
        }
        if (firstDisplayedLineIdx > 0) { // they've scrolled down, so need indicator they can scroll up
            directions += OComponent.UP;
        }
//        if (Debug.ON) {
//            Logger.dev("OTextScreen.getScrollDirections EXITTING");
//        }
        return directions;
        
    } // getScrollDirections
    
    /**
     * Handles key presses.
     *
     * @param keyCode The code of the key that was pressed
     */
    public void keyPressed(int keyCode) {

        if (keyCode == DOWN_KEY) {
            
            moveVertically(1);
            repaint();
        } else if (keyCode == UP_KEY) {

            moveVertically(-1);
            repaint();
        }
       else {
            super.keyPressed(keyCode);
        }
//        }
        
    } // keyPressed
   
      
    /**
     * Moves the cursor vertically.
     *
     * @param nbrLines The number of lines to move. A negative value indicates
     * move up, a positive value means move down.
     */
    private void moveVertically(int nbrLines) {
        
        if(nbrLines == 1 &&  lineEndIndices[editingLineIdx] > textBuff.length( ) ){
            return;
        }
        
        int newLineIdx = editingLineIdx + nbrLines;
        
        if(newLineIdx >= 0  ){
            if (nbrLines > 0 && lineEndIndices[newLineIdx] == 0) { // this is the first time this line is being displayed, so we need to
                // determine its last character
                lineEndIndices[newLineIdx] =
                        getLineEnd(lineEndIndices[editingLineIdx] + 1);
            }
            
            // don't change editingLineIdx until the end, because it is used
            // by getCursorX in finding the x coordinate before moving
            editingLineIdx += nbrLines;
            if (editingLineIdx < firstDisplayedLineIdx ||
                    editingLineIdx > lastDisplayedLineIdx) {
                scroll(nbrLines);
            }
        }
    } // moveVertically
    
  
    /**
     * Scrolls the display.
     *
     * @param nbrLines Number of lines to scroll. A negative number means scroll
     * up, a positive number means scroll down.
     */
    private void scroll(int nbrLines) {
        
        firstDisplayedLineIdx += nbrLines;
        lastDisplayedLineIdx += nbrLines;
        updateLineIndices(firstDisplayedLineIdx);
        
    } // scroll
    
    /**
     * Updates lineEndIndices, starting at the supplied index and continuing to
     * the last displayable (meaning it will fit on the screen) line.
     *
     * @param startingLineIdx The index of the first line to update
     */
    private void updateLineIndices(int startingLineIdx) {
        if (Debug.ON) {
            Logger.dev("OTextScreen.updateLineIndices ENTERED ");
        }
        int textBuffEnd = textBuff.length() - 1;
        
        
        if (textBuffEnd < 0) {
            return; // no text to display
        }
        // update the index for each displayed line after and including
        // startingLIneIdx
        int lineStart = -1;
        int lineEnd = -1;
        for (lastDisplayedLineIdx = startingLineIdx;
        lineEnd != textBuffEnd &&
                lastDisplayedLineIdx - firstDisplayedLineIdx < nbrDisplayableLines;
        lastDisplayedLineIdx++) {
            lineStart = getLineStart(lastDisplayedLineIdx);
            lineEnd = getLineEnd(lineStart);
            lineEndIndices[lastDisplayedLineIdx] = lineEnd;
        }
        lastDisplayedLineIdx--;  // remove last increment
        
        
    } // updateLineIndices
    
    /**
     * Gets the index in textBuff of the beginning of a line.
     *
     * @param lineIdx The index in lineEndIndices of the line.
     * @return an <code>int</code> value
     */
    private int getLineStart(int lineIdx) {
        
        int lineStart = 0;
        // get the beginning index, within the text buffer, of the line
        if (lineIdx > 0) { // this line starts at the first character after the end of the last line,
            // unless the first character is a space and the second character isn't a
            // space, in which case we ignore the space so line's don't start with a
            // space (but a line can start with multiple spaces, since that may be
            // indentation)
            lineStart = lineEndIndices[lineIdx - 1] +1;
            
            int textBuffEnd = textBuff.length() - 1;
            if (lineStart < textBuffEnd && textBuff.charAt(lineStart) == ' ' && lineStart + 1 < textBuffEnd && textBuff.charAt(lineStart + 1) != ' ') {
                lineStart++;
            }
        }
        // else  lineIdx == 0 and line always starts on 0
        
        if (Debug.ON) {
            Logger.dev("OTextScreen.getLineStart EXITTING");
        }
        
        return lineStart;
    } // getLineStart
    
    /**
     * Gets the index in textBuff of the last character in a line.
     *
     * @param lineStart The beginning index, in textBuff, of the line.
     * @return an <code>int</code> value
     */
    private int getLineEnd(int lineStart) {
        
        int textBuffEnd = textBuff.length() - 1;
        
        // find the last character that will fit in the screen width
        int lineEnd = lineStart;
        int lineLength = 0;
        
        while (true && lineEnd < textBuff.length()) {
            //System.out.println("lineEnd=" + lineEnd);
            lineLength += textFont.charWidth(textBuff.charAt(lineEnd));
            if (lineLength > screenWidth) {
                lineEnd--;
                break;
            } else if (lineEnd == textBuffEnd) {
                break;
            } else {
                lineEnd++;
            }
        }
        
        // we don't want to break in the middle of any contiguous characters, so if
        // the line isn't followed by a space back up to the last space
        //System.out.println("lineEnd=" + lineEnd);
        if ( lineEnd + 1 < textBuff.length() && (lineEnd < textBuffEnd && textBuff.charAt(lineEnd + 1) != ' ') ) {
            int testEnd = lineEnd;
            for (; testEnd > lineStart; testEnd--) {
                if (textBuff.charAt(testEnd) == ' ') { // found space
                    lineEnd = testEnd;
                    break;
                }
            }
        }
        
        return lineEnd;
    } // getLineEnd
    
    /**
     * Determines whether a line is the last line in textBuff.
     *
     * @param lineIdx The index in lineEndIndices of the line to check.
     * @return true if the line is the last line
     */
    private boolean lastLine(int lineIdx) {
        if (Debug.ON) {
            Logger.dev("OTextScreen.lastLine CALLED");
        }
        return (textBuff.length() == 0 ||
                lineEndIndices[lineIdx] == textBuff.length() - 1);
    } // lastLine
    
   
    
    /**
     * Gets the number of characters in the document
     *
     * @return an <code>int</code> value
     */
    public int length() {
        return textBuff.length();
    } // length
    

    /**
     * Gets the special characters that the document will accept.
     *
     * @return the special characters that the document will accept
     */
    public String getSpecialChars() {
        if (Debug.ON) {
            Logger.dev("OTextScreen.getSpecialChars ENTERED");
        }
        return specialChars;
    } // getSpecialChars

    /**
     * Sets the text in the document.
     *
     * @param value The document text
     * @exception InvalidData if the text exceeds the length of the field or
     * contains invalid characters.
     */
    public void setText(String value) {
        
        if (value.length() > 0) {

            textBuff = new StringBuffer(value);
            textBuff.ensureCapacity(nbrOfChars);
        }
        
        firstDisplayedLineIdx = 0;
        lastDisplayedLineIdx = 0;
        lineEndIndices[0] = 0;
        editingLineIdx = 0 ;
        updateLineIndices(0);
        repaint();
        
    } // setText
  
    /**
     * Gets the document's text.
     *
     * @return a <code>String</code> value
     */
    public String getText() {
        return textBuff.toString();
    } // getText
    
    public void addPosicionTravers(int keyCode) {
        
        keyPressed(keyCode);
    }
    
}// OTextScreen

